# Generates a rule tree using a given Python transition function
# passed in via the clipboard.  Requires Golly 2.1 or later.
# Author: Tim Hutton (tim.hutton@gmail.com), based on Tom Rokicki's
# RuleTreeGen code in the Rules/TreeGenerators directory.

# Here's an example function for Conway's Life
# (copy all the lines between the triple quotes):
'''
# B3/S23:
name = "LifeTest"
n_states = 2
n_neighbors = 8
def transition_function(s):
    # order for 8 neighbors is NW, NE, SW, SE, N, W, E, S, C
    n = s[0]+s[1]+s[2]+s[3]+s[4]+s[5]+s[6]+s[7]
    if n==2 and s[8]==1:
        return 1 
    if n==3:
        return 1
    return 0
'''

# Another example, but using the vonNeumann neighborhood:
'''
name = "ParityNWE"
n_states = 5
n_neighbors = 4
def transition_function (s):
    # order for 4 neighbors is N, W, E, S, C
    N, W, E, S, C = s
    return ( N + W + E ) % 5
'''

import golly

class GenerateRuleTree:

    def __init__(self,numStates,numNeighbors,transFunc):
        self.numParams = numNeighbors + 1
        self.world = {}
        self.r = []
        self.params = [0]*self.numParams
        self.nodeSeq = 0
        self.numStates = numStates
        self.numNeighbors = numNeighbors
        self.transFunc = transFunc
        self.recur(self.numParams)

    def getNode(self,n):
        if n in self.world:
            return self.world[n]
        else:
            new_node = self.nodeSeq
            self.nodeSeq += 1
            self.r.append(n)
            self.world[n] = new_node
            return new_node

    def recur(self,at):
        if at == 0:
            return self.transFunc(self.params)
        n = str(at)
        for i in xrange(self.numStates):
           # report progress
           if at == self.numParams:
              golly.show('Generating rule tree: '+str(int(100*i/self.numStates))+'%...')
           # allow golly to update
           if at == self.numParams-1:
              golly.update()
           self.params[self.numParams-at] = i
           n += " " + str(self.recur(at-1))
        return self.getNode(n)

    def writeRuleTree(self,name):
        # create a .tree file in user's rules directory
        f=open(golly.getdir("rules")+name+".tree", 'w')
        f.write("# Automatically generated by make-ruletree.py.\n")
        f.write("num_states=" + str(self.numStates)+"\n")
        f.write("num_neighbors=" + str(self.numNeighbors)+"\n")
        f.write("num_nodes=" + str(len(self.r))+"\n")
        for rule in self.r:
            f.write(rule+"\n")
        f.flush()                   # ensure file is complete (only on Windows?)
        f.close()
        golly.setalgo("RuleTree")   # in case name.table exists
        golly.setrule(name)
        golly.show("Created "+name+".tree in "+golly.getdir("rules"))

# exec() only works if all lines end with LF, so we need to convert
# any Win line endings (CR+LF) or Mac line endings (CR) to LF
CR = chr(13)
LF = chr(10)
try:
   
   exec(golly.getclipstr().replace(CR+LF,LF).replace(CR,LF))
   gen = GenerateRuleTree(n_states, n_neighbors, transition_function)
   gen.writeRuleTree(name)
   
except:
   import sys
   import traceback
   exception, msg, tb = sys.exc_info()
   golly.warn(\
'''To use this script, copy a Python format rule definition into the clipboard, e.g.:

name = "ParityNWE"
n_states = 5
n_neighbors = 4
# order for 8 neighbors is NW, NE, SW, SE, N, W, E, S, C
# order for 4 neighbors is N, W, E, S, C
def transition_function ( s ) : 
      return ( s[0] + s[1] + s[2] ) % 5

For more examples, see the script file (right-click on it).
____________________________________________________

A problem was encountered with the supplied rule:

'''+ '\n'.join(traceback.format_exception(exception, msg, tb)))
   golly.exit()
